using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace Doppler.Core.Plugin {
  public class PluginFactory {
    List<IPlugin> _plugins = null;

    /// <summary>
    /// Initializes a new instance of the <see cref="PluginFactory"/> class.
    /// </summary>
    public PluginFactory ( ) {
      _plugins = new List<IPlugin> ( );
    }

    /// <summary>
    /// Gets the plugins.
    /// </summary>
    /// <value>The plugins.</value>
    public List<IPlugin> Plugins {
      get { return this._plugins; }
    }

    /// <summary>
    /// Finds the plugin by the specified name.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <returns></returns>
    public IPlugin Find ( string name ) {
      foreach ( IPlugin plug in this.Plugins ) {
        if ( string.Compare ( name, plug.ProductName, true ) == 0 )
          return plug;
      }
      return null;
    }

    /// <summary>
    /// Finds the plugin by the specified type.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns></returns>
    public IPlugin Find ( Type type ) {
      foreach ( IPlugin plug in this.Plugins ) {
        if ( plug.GetType ( ) == type )
          return plug;
      }
      return null;
    }

    /// <summary>
    /// Finds the plugin by the specified GUID.
    /// </summary>
    /// <param name="guid">The GUID.</param>
    /// <returns></returns>
    public IPlugin Find ( Guid guid ) {
      foreach ( IPlugin plug in this.Plugins ) {
        if ( string.Compare ( plug.Guid.ToString ( ), guid.ToString ( ), true ) == 0 )
          return plug;
      }
      return null;
    }
    /// <summary>
    /// Loads the plugins.
    /// </summary>
    /// <param name="path">The path.</param>
    /// <param name="recursive">if set to <see langword="true"/> [recursive].</param>
    /// <returns></returns>
    public void Load ( DirectoryInfo path, bool recursive ) {
      foreach ( FileInfo file in path.GetFiles ( "*.exe", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly ) ) {
        try {
          this.Plugins.AddRange ( LoadAssembly ( file ) );
        } catch { }
      }

      foreach ( FileInfo file in path.GetFiles ( "*.dll", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly ) ) {
        try {
          this.Plugins.AddRange ( LoadAssembly ( file ) );
        } catch { }
      }

    }

    /// <summary>
    /// Loads the plugins.
    /// </summary>
    /// <param name="path">The path.</param>
    /// <returns></returns>
    public void Load ( DirectoryInfo path ) {
      Load ( path, true );
    }

    /// <summary>
    /// Loads the plugins.
    /// </summary>
    /// <returns></returns>
    public void Load ( ) {
      DirectoryInfo path = new DirectoryInfo ( Path.GetDirectoryName ( this.GetType ( ).Assembly.Location ) );
      this.Load ( path );
    }

    /// <summary>
    /// Loads the assembly.
    /// </summary>
    /// <param name="dll">The DLL.</param>
    /// <returns></returns>
    public List<IPlugin> LoadAssembly ( FileInfo dll ) {
      List<IPlugin> plugins = new List<IPlugin> ( );
      Assembly asm = Assembly.LoadFile ( dll.FullName );
      Type[ ] types = asm.GetTypes ( );
      foreach ( Type t in types ) {
        // check that the type is a plugin.
        if ( t.GetInterface ( typeof ( IPlugin ).FullName, false ) != null && !t.IsAbstract && t.IsPublic && !t.IsInterface ) {
          // create an instance of the plugin
          IPlugin iplug = CreateInstanceOfPlugin ( t );
          if ( iplug != null ) {
            // also want to check that we dont already have an instance of this plugin already created and 
            // in the collection.
            if( this.Find( iplug.Guid ) == null )
              plugins.Add ( iplug );
          }
        }
      }
      return plugins;
    }

    /// <summary>
    /// Creates the instance of plugin.
    /// </summary>
    /// <param name="t">The t.</param>
    /// <returns></returns>
    private IPlugin CreateInstanceOfPlugin ( Type t ) {
      if ( t != null ) {
        try {
          return t.Assembly.CreateInstance ( t.FullName, true ) as IPlugin;
        } catch {
          return null;
        }
      } else
        return null;
    }
  }
}
